package com.jmxcfc.blfsc.ssq.service.contract.impl;

import com.alibaba.fastjson.JSONObject;
import com.jmxcfc.blfsc.common.common.enums.ApiStatusEnum;
import com.jmxcfc.blfsc.common.common.enums.ProcessStateEnum;
import com.jmxcfc.blfsc.common.common.request.BaseRequest;
import com.jmxcfc.blfsc.common.common.response.BaseResponse;
import com.jmxcfc.blfsc.common.common.utils.FileUtils;
import com.jmxcfc.blfsc.common.exception.AfterOutboundException;
import com.jmxcfc.blfsc.common.exception.BeforeOutboundException;
import com.jmxcfc.blfsc.common.exception.OutboundException;
import com.jmxcfc.blfsc.common.sign.SignRequest;
import com.jmxcfc.blfsc.common.sms.BaseContext;
import com.jmxcfc.blfsc.common.validation.rule.RequestFieldRule;
import com.jmxcfc.blfsc.common.validation.rule.SignRulesUtil;
import com.jmxcfc.blfsc.ssq.common.Constants;
import com.jmxcfc.blfsc.ssq.entity.DownloadContractSub;
import com.jmxcfc.blfsc.ssq.service.SignatureService;
import com.jmxcfc.blfsc.ssq.service.contract.IDownloadContractRequestService;
import com.jmxcfc.blfsc.ssq.service.contract.IDownloadContractSubService;
import com.jmxcfc.blfsc.ssq.service.contract.ILockContractSubService;
import com.jmxcfc.blfsc.ssq.util.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;

/**
 * 功能描述
 *
 * @author: pbs
 * @date: 2024年09月04日 0:21
 */
@Service
@Slf4j
public class DownloadContractRequestServiceImpl implements IDownloadContractRequestService {

    //请求地址
    @Value("${com.ssq.downloadContract.url}")
    private String downloadContractUrl;

    //签章后文档保存地址的前缀
    @Value("${com.ssq.downloadContract.outputFilepath}")
    private String outputFilepath;




    private SignatureService signatureService;

    private ILockContractSubService lockContractSubService;
    private IDownloadContractSubService downloadContractSubService;

    private RestTemplate restTemplate;


    @Autowired
    public void setSignatureService(SignatureService signatureService) {
        this.signatureService = signatureService;
    }

    @Autowired
    public void setLockContractSubService(ILockContractSubService lockContractSubService) {
        this.lockContractSubService = lockContractSubService;
    }

    @Autowired
    public void setDownloadContractSubService(IDownloadContractSubService downloadContractSubService) {
        this.downloadContractSubService = downloadContractSubService;
    }

    @Autowired
    public void setRestTemplate(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    /**
     * 参数:需要合同模板路径、产品号,重这里获取到文件名称
     * @param requestMessage
     * @return
     */
    @Override
    public BaseResponse<JSONObject> sendRequest(BaseRequest<SignRequest<String>> requestMessage) {
        BaseContext<byte[]>  baseContext = new BaseContext<>();
        //头部参数校验规则:业务号
        List<RequestFieldRule<?>> headerElementRules = baseContext.getHeaderElementRules();
        headerElementRules.add(SignRulesUtil.getRequestNoRule());
        List<RequestFieldRule<?>> bodyElementRules = baseContext.getBodyElementRules();
        //bodyElementRules.add(SignRulesUtil.getIdCodeRule());
        bodyElementRules.add(SignRulesUtil.getTemplePdfPathRule());

        return execute(requestMessage, baseContext);
    }

    /**
     * contractId取值于下载合同文件接口的表 SSQ_LOCK_CONTRACT_SUB 最新且成功记录
     * @param request
     * @param response
     * @param baseContext
     * @throws BeforeOutboundException
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void beforeOutbound(BaseRequest<SignRequest<String>> request, BaseResponse<JSONObject> response, BaseContext<byte[]> baseContext) throws BeforeOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        try {
            log.info("请求业务号requestNo:{},下载合同文件接口,前置处理:数据落库和参数组装", requestNo);
            DownloadContractSub downloadContractSub = new DownloadContractSub();
            downloadContractSub.setRequestNo(requestNo);

            //参数组织、regUserPersonalSub设置值
            String urlParams = buildParam(request, downloadContractSub);

            //由于是签名参数追加为url参数:/openapi/v2/user/reg/?developerId=aaaaaa&rtick=14840209310001&signType=rsa
            String urlPathParams = signatureService.buildUrlPathParams(downloadContractUrl, urlParams, "");

            //请求路径的url的参数
            baseContext.setExtendStr(urlPathParams);
            //设置请求
            baseContext.setRestHttpEntity(HttpUtils.creatRequestEntity(""));
            //保存数据
            downloadContractSubService.save(downloadContractSub);
            //主键
            baseContext.setRecordId(downloadContractSub.getId());
            JSONObject responseBody = new JSONObject();
            responseBody.put(Constants.CONTRACT_ID, downloadContractSub.getContractId());
            response.setBody(responseBody);
            log.info("请求业务号requestNo:{},下载合同文件接口,前置处理结束", requestNo);
        } catch (Exception e) {
            log.error("请求业务号requestNo:{},前置处理异常:{}", requestNo, e.getMessage(), e);
            throw new BeforeOutboundException(e.getMessage(), e);
        }
    }

    /**
     * get请求,参数拼接需要注意,和post不同
     *
     * @param request
     * @param response
     * @param baseContext
     * @throws OutboundException
     */
    @Override
    public void outbound(BaseRequest<SignRequest<String>> request, BaseResponse<JSONObject> response, BaseContext<byte[]> baseContext) throws OutboundException {
        try {
            //请求url的,必须要用URL,否则restTemplate会对url进行
            URI requestUrl = new URI(StringUtils.join(downloadContractUrl, baseContext.getExtendStr()));
            log.info("请求业务号requestNo:{},下载合同文件接口,发送请求外呼,请求地址:{},请参数:{}", request.getHeader().getRequestNo(), requestUrl, baseContext.getRestHttpEntity().getBody());
            ResponseEntity<byte[]> respEntity = restTemplate.getForEntity(requestUrl, byte[].class);
            log.info("请求业务号requestNo:{},下载合同文件接口,发送请求外呼,响应内容:{}", request.getHeader().getRequestNo(), respEntity);
            baseContext.setRestResponse(respEntity.getBody());
        } catch (Exception e) {
            log.error("请求业务号requestNo:{},下载合同文件接口,发送请求外呼异常:{}", request.getHeader().getRequestNo(), e.getMessage());
            updateWhenApiException(baseContext.getRecordId(), request.getHeader().getRequestNo(), e.getMessage());
            throw new OutboundException(e.getMessage(), e);
        }
    }


    /**
     * 异常时更新数据
     * @param id
     * @param requestNo
     * @param msg
     */
    private void updateWhenApiException(String id,String requestNo,String msg) {
        try {
            DownloadContractSub downloadContractSub = new DownloadContractSub();
            downloadContractSub.setId(id);
            //downloadContractSub.setRequestNo(requestNo);
            downloadContractSub.setApiStatus(ApiStatusEnum.EXCEPTION.getStatus());
            downloadContractSub.setApiStatusDesc(StringUtils.substring(msg, 0, 300));
            downloadContractSubService.updateById(downloadContractSub);
        } catch (Exception e) {
            log.error("下载合同文件接口,外呼异常时更新数据库失败,订单号:{}", requestNo);
        }
    }

    /**
     * 1、读取合同文件流,合同文件路径本地地址:
     outputFilepath/datePath/prodCode/文件名称

     签署后文件pdf(localPdfPath)写到(共享盘):/根目录/yyyyMMdd/产品/文件名.pdf

     根目录(配置application)

     备注其中文件名称:截取合同模板文件名称
     *2、响应体的body返回:合同id(contractId)、合同签署后的pdf文件地址(pdfFilePath)
     * 3、
     *  @param request
     * @param response
     * @param baseContext
     * @throws AfterOutboundException
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void afterOutbound(BaseRequest<SignRequest<String>> request,  BaseResponse<JSONObject> response, BaseContext<byte[]>  baseContext) throws AfterOutboundException {
        String requestNo = request.getHeader().getRequestNo();
        try{
            log.info("请求业务号requestNo:{},下载合同文件接口,后置处理:解析响应结果", requestNo);
            DownloadContractSub downloadContractSub = new DownloadContractSub();
            downloadContractSub.setRequestNo(requestNo);
            //注意这边是根据主键更新
            downloadContractSub.setId(baseContext.getRecordId());
            downloadContractSub.setApiStatus(ApiStatusEnum.SUCCESS.getStatus());
            downloadContractSub.setApiStatusDesc(ApiStatusEnum.SUCCESS.getDesc());

            byte[] restResponse = baseContext.getRestResponse();
            SignRequest<String> signRequestBody = request.getBody();
            if (Objects.nonNull(restResponse)) {
                //写文件
                String localOutputFilepath = FileUtils.createOutputFilepath(outputFilepath,signRequestBody.getProdCode(),signRequestBody.getTemplePdfPath());
                log.info("请求业务号requestNo:{},开始下载合同文档保存地址:{}", requestNo, localOutputFilepath);
                Files.write(Paths.get(localOutputFilepath), restResponse);
                log.info("请求业务号requestNo:{},成功下载合同文档保存地址:{}", requestNo, localOutputFilepath);
                downloadContractSub.setPdfFilePath(localOutputFilepath);
                response.getHeader().setProcessState(ProcessStateEnum.SUCCESS);
            }else {
                log.error("请求业务号requestNo:{},PDF文件流为空,下载失败",requestNo);
                // TODO: 2024/9/10 状态待确认
                response.getHeader().setProcessState(ProcessStateEnum.FAILURE);
            }
            downloadContractSubService.updateById(downloadContractSub);
            response.getHeader().setResCode("");
            response.getHeader().setResMessage("");
            //response在befor模块,初始化值和设置了合同编号key的值
            response.getBody().put(Constants.PDF_FILE_PATH, downloadContractSub.getPdfFilePath());
            log.info("请求业务号requestNo:{},上传并创建合同接口接口,后置处理结束", requestNo);
        } catch (Exception e) {
            log.error("请求业务号requestNo:{},上传并创建合同接口接口,后置处理异常:{}", requestNo, e.getMessage(), e);
            throw new AfterOutboundException(e.getMessage(), e);
        }
    }


    /**
     * 参数组装,是get请求,所以参数是拼接在url后面
     * @param request
     * @param downloadContractSub
     * @return
     */
    private String buildParam(BaseRequest<SignRequest<String>> request, DownloadContractSub downloadContractSub) {
        String requestNo = request.getHeader().getRequestNo();
        String contractId = lockContractSubService.queryLatestContractId(requestNo);
        if (StringUtils.isBlank(contractId)) {
            log.error("根据请求业务号requestNo:{},查询锁定合同接口记录表 SSQ_LOCK_CONTRACT_SUB 的合同id,查询结果为空",requestNo);
            throw new BeforeOutboundException("未查询到合同id");
        }
        downloadContractSub.setContractId(contractId);
        String urlParams = "contractId=" + contractId;
        log.info("请求业务号requestNo:{},参数组织完成,参数:{}", request.getHeader().getRequestNo(), urlParams);
        return urlParams;
    }


}